﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using Fiddler;
using Sword.Data;
using Sword.ProxyCore.RuleMatcher;
using Sword.ProxyCore.RuleParser;
using Sword.Utility;
using System.Net.NetworkInformation;
using System.Windows.Forms;
using Sword.ProxyCore.CmdExecute;

namespace Sword.ProxyCore
{
    public enum ServiceCommandType
    {
        None = 0,

        Config = 1,

        InjectConfig = 2,

        Start = 3,

        ShutDown = 4,

        Pause = 10,

        ApplicationExit = 88
    }

    /// <summary>
    /// 委托:通知消息
    /// </summary>
    /// <param name="message">消息内容</param>
    public delegate void UpdateMessageDelegate(string message);
    /// <summary>
    /// 委托:代理核心服务缺省委托
    /// </summary>
    public delegate void CoreServiceDelegate();
    /// <summary>
    /// 委托:加载制动规则
    /// </summary>
    /// <param name="rules">制动规则列表</param>
    public delegate void LoadRulesDelegate(List<ConditionItem> rules);

    /// <summary>
    /// 代理核心服务
    /// <pre>包括请求拦截、修改报文等，基于FiddlerCore开发</pre>
    /// </summary>
    public class CoreService
    {
        /// <summary>
        /// 后台线程
        /// </summary>
        System.Threading.Thread mainThread = null;
        /// <summary>
        /// 单例-待执行命令
        /// </summary>
        ServiceCommandType commandName = ServiceCommandType.Config;
        /// <summary>
        /// 单例-服务暂停命令
        /// </summary>
        string pauseServiceCommandName = "";
        /// <summary>
        /// 程序配置
        /// </summary>
        AppConfigRootEntity appConfig = null;
        /// <summary>
        /// 程序制动规则列表
        /// </summary>
        public List<ConditionItem> Rules { get; set; }
        /// <summary>
        /// 通知界面程序，有警告
        /// </summary>
        public UpdateMessageDelegate CallbackWarning { get; set; }
        /// <summary>
        /// 更新引用程序界面消息的委托
        /// </summary>
        public UpdateMessageDelegate CallbackUpdateMessage { get; set; }
        /// <summary>
        /// 服务开启结束通知委托
        /// </summary>
        public CoreServiceDelegate StartCompleted { get; set; }
        /// <summary>
        /// 服务关闭结束通知委托
        /// </summary>
        public CoreServiceDelegate StopCompleted { get; set; }
        /// <summary>
        /// 规则加载、再加在完毕通知委托
        /// </summary>
        public LoadRulesDelegate RulesLoadCompleted { get; set; }


        /// <summary>
        /// 构造：注入程序配置信息
        /// </summary>
        /// <param name="config">注入程序配置信息</param>
        public CoreService(AppConfigRootEntity config)
        {
            if (config == null)
            {
                this.appConfig = XmlUtility.LoadConfigs();
            }
            else
            {
                this.appConfig = config;
            }

            this.Rules = TextRuleParser.Convert(appConfig);

        }

        object lockObject = new object();

        /// <summary>
        /// 启动服务
        /// </summary>
        public void Start()
        {
            if (mainThread == null)
            {
                mainThread = new System.Threading.Thread(() => { ThreadProcess(); });
                mainThread.IsBackground = true;
                mainThread.Start();
            }
            else
            {
                UpdateMessage("已有线程在执行了，若要再启动，请重启程序，谢谢！");
            }
        }
        
        void ThreadProcess()
        {

            bool needShutdown = false;
            while (!needShutdown)
            {
                switch (commandName)
                {
                    case ServiceCommandType.Config:
                        Config();
                        break;
                    case ServiceCommandType.InjectConfig:
                        InjectConfig();
                        commandName = ServiceCommandType.None;
                        break;
                    case ServiceCommandType.Start:
                        Startup();
                        commandName = ServiceCommandType.None;
                        if (StartCompleted != null)
                        {
                            StartCompleted();
                        }
                        break;
                    case ServiceCommandType.ShutDown:
                        ShutDown();
                        commandName = ServiceCommandType.None;
                        if (StopCompleted != null)
                        {
                            StopCompleted();
                        }
                        break;
                    case ServiceCommandType.ApplicationExit:
                        ShutDown();
                        needShutdown = true;
                        commandName = ServiceCommandType.None;
                        if (StopCompleted != null)
                        {
                            StopCompleted();
                        }
                        break;
                    case ServiceCommandType.Pause:
                        Fiddler.FiddlerApplication.oProxy.Detach();
                        break;
                    default:
                        break;
                }
                Thread.Sleep(100);
            }
        }

        void Config()
        {
            Fiddler.FiddlerApplication.SetAppDisplayName("FrondEndSword");
            Fiddler.CONFIG.IgnoreServerCertErrors = false;
            FiddlerApplication.Prefs.SetBoolPref("fiddler.network.streaming.abortifclientaborts", true);
            //FiddlerApplication.Prefs.SetBoolPref("fiddler.network.SetHostHeaderFromURL", false);

            Fiddler.FiddlerApplication.OnNotification += FiddlerApplication_OnNotification;
            Fiddler.FiddlerApplication.Log.OnLogString += Log_OnLogString;
            Fiddler.FiddlerApplication.BeforeRequest += FiddlerApplication_BeforeRequest;
            //Fiddler.FiddlerApplication.BeforeReturningError += FiddlerApplication_BeforeReturningError;
            Fiddler.FiddlerApplication.BeforeResponse += FiddlerApplication_BeforeResponse;

            LoadRules();
            LoadCmdProcess();
            commandName = ServiceCommandType.Start;
            if (CallbackUpdateMessage != null)
            {
                CallbackUpdateMessage("内核配置初始化结束，等待启动。");
            }
        }

        Random random = new Random();

        void FiddlerApplication_BeforeRequest(Session oSession)
        {
            try
            {
                #region [BeforeRequest]
#if DEBUG
                lock (lockObject)
                {
#endif
                    if (commandName == ServiceCommandType.Pause)
                    {
                        oSession.bypassGateway = true;
                        return;
                    }

                    bool isMatched = false;
                    ConditionItem targetRule = null;

                    //IRuleMatcher matcher = null;
                    //List<IRuleMatcher> matchers = null;
                    foreach (var rule in this.Rules)
                    {
                        UrlMatcher urlMatcher = new UrlMatcher();
                        //matcher = ruleMatcher.Where(p => p.Type == rule.ConditionType).FirstOrDefault();

                        if (urlMatcher.Type == rule.ConditionType && urlMatcher.IsMatch(oSession, rule))
                        {

                                targetRule = rule;
                            
                            isMatched = true;
                            break;
                        }

                    }

                    if (!isMatched)
                    {
                        oSession.bypassGateway = true;
                        return;
                    }

                    //oSession.oRequest.headers.Add("SOA20-Client-AppId", "110102");

                    //BeforeResponse的修改要生效，需要这句
                    oSession.bBufferResponse = true;

                    Fiddler_OnBeforeRequest_ChangeSession(oSession, targetRule);
#if DEBUG
                }
#endif
                #endregion
            }
            catch (Exception ex)
            {
                CallbackUpdateMessage("请求处理报错:" + ex.Message);
            }
        }

        void FiddlerApplication_BeforeResponse(Session oSession)
        {
            DoResponse(oSession, false);
        }

        void DoResponse(Session oSession, bool isErrorHandler)
        {
            try
            {
                #region [BeforeResponse]


                if (isErrorHandler && (oSession.oResponse.headers.HTTPResponseCode == 408 || oSession.oResponse.headers.HTTPResponseCode == 502))
                {
                    oSession["custom-timeout"] = "1";
                }

                if (pauseServiceCommandName == "pause")
                {
                    return;
                }

                #endregion
            }
            catch (Exception ex)
            {
                CallbackUpdateMessage("请求处理报错:" + ex.Message);
            }
        }

        void FiddlerApplication_OnNotification(object sender, NotificationEventArgs e){}
        void Log_OnLogString(object sender, LogEventArgs e){}

        void InjectConfig()
        {
            LoadRules();
            if (CallbackUpdateMessage != null)
            {
                CallbackUpdateMessage("请求重定向规则更新完毕。");
            }
        }

        void Startup()
        {
            //FiddlerApplication.Prefs.SetStringPref("fiddler.config.path.makecert", @"d:\..\Makecert.exe");//To define the MakeCert.exe path manually.
            //FiddlerApplication.Prefs.SetBoolPref("fiddler.network.streaming.abortifclientaborts", true);//Abort session when client abort
            //FiddlerApplication.Prefs.SetStringPref("fiddler.proxy.pacfile.text", "your script body goes here...");
            try
            {


                //Fiddler.URLMonInterop.SetProxyInProcess("127.0.0.1:8888", null);
                //URLMonInterop.SetProxyInProcess("127.0.0.1:8789", "<-loopback>");
                //Fiddler.CONFIG.iReverseProxyForPort = 33334;
                bool enabledAutoSetSystemProxy = appConfig != null;
                int port = appConfig != null ? appConfig.ProxyPort : 33333;


                if (PortInUse(port))
                {
                    string msg = "端口被占用，请更换其他端口，强行使用，会导致ip重定向失败，" + Environment.NewLine
                                + "建议1）在设置界面修改端口号" + Environment.NewLine
                                + "建议2）在修改完端口号的基础上，在软件里取消系统代理的设置，保存设置，重启软件，然后手动修改浏览器的代理" + Environment.NewLine
                                + "争议端口号：" + appConfig.ProxyPort.ToString() + "。";
                    UpdateMessage("端口被占用，争议端口号：" + port.ToString());
                    MessageBox.Show(msg, "端口被占用", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                    return;
                }

                //Fiddler.FiddlerApplication.Startup(port, enabledAutoSetSystemProxy, true, true);
                var flags = //FiddlerCoreStartupFlags.DecryptSSL |
                           FiddlerCoreStartupFlags.AllowRemoteClients |
                           FiddlerCoreStartupFlags.ChainToUpstreamGateway |
                           FiddlerCoreStartupFlags.MonitorAllConnections |
                           FiddlerCoreStartupFlags.CaptureLocalhostTraffic |
                           FiddlerCoreStartupFlags.OptimizeThreadPool |
                           FiddlerCoreStartupFlags.CaptureLocalhostTraffic |
                           (enabledAutoSetSystemProxy ? FiddlerCoreStartupFlags.RegisterAsSystemProxy : FiddlerCoreStartupFlags.None);

                Fiddler.FiddlerApplication.Startup(port, flags);
                Fiddler.URLMonInterop.SetProxyInProcess("127.0.0.1:" + port.ToString(), "<-loopback>");

                // 修改WebRequest的默认代理。
                //WebRequest.DefaultWebProxy = new WebProxy("localhost", port);
                WebRequest.DefaultWebProxy = null;

                ////oSecureEndpoint = FiddlerApplication.CreateProxyEndpoint(iSecureEndpointPort, true, sSecureEndpointHostname);
                //if (null != oSecureEndpoint)
                //{
                //    FiddlerApplication.Log.LogFormat("Created secure endpoint listening on port {0}, using a HTTPS certificate for '{1}'", iSecureEndpointPort, sSecureEndpointHostname);
                //}
                UpdateMessage("服务已经启动好，可以使用了。请确保浏览器的代理是127.0.0.1:" + appConfig.ProxyPort.ToString() + "。");
            }
            catch (Exception ex)
            {
                if (CallbackUpdateMessage != null)
                {
                    CallbackUpdateMessage("服务启动失败！错误消息：" + Environment.NewLine + ex.Message);
                }
            }
            //var flags= FiddlerCoreStartupFlags.DecryptSSL & 
            //           FiddlerCoreStartupFlags.AllowRemoteClients &
            //           FiddlerCoreStartupFlags.CaptureFTP & 
            //           FiddlerCoreStartupFlags.ChainToUpstreamGateway &
            //           FiddlerCoreStartupFlags.MonitorAllConnections &
            //           FiddlerCoreStartupFlags.CaptureLocalhostTraffic;
            //Fiddler.FiddlerApplication.Startup(33333, flags);
            //oSecureEndpoint = FiddlerApplication.CreateProxyEndpoint(iSecureEndpointPort, true, sSecureEndpointHostname);
            //if (null != oSecureEndpoint)
            //{
            //    FiddlerApplication.Log.LogFormat("Created secure endpoint listening on port {0}, using a HTTPS certificate for '{1}'", iSecureEndpointPort, sSecureEndpointHostname);
            //}
        }

        public bool PortInUse(int port)
        {
            bool inUse = false;

            IPGlobalProperties ipProperties = IPGlobalProperties.GetIPGlobalProperties();
            IPEndPoint[] ipEndPoints = ipProperties.GetActiveTcpListeners();

            foreach (IPEndPoint endPoint in ipEndPoints)
            {
                if (endPoint.Port == port)
                {
                    inUse = true;
                    break;
                }
            }
            return inUse;
        }

        public void ShutDown()
        {
            try
            {
                if (Fiddler.FiddlerApplication.isClosing)
                {
                    UpdateMessage("服务正在关闭中……");
                    return;
                }

                commandName = ServiceCommandType.None;
                //プロキシ設定を外す
                Fiddler.URLMonInterop.ResetProxyInProcessToDefault();

                Fiddler.URLMonInterop.SetProxyDisabledForProcess();

                if (Fiddler.FiddlerApplication.oProxy != null)
                {
                    Fiddler.FiddlerApplication.oProxy.Detach();
                }

                Fiddler.FiddlerApplication.Shutdown();

                IEProxyInjecter.SetProxy(string.Empty, false);
                //UpdateMessage("服务已经被关闭！");
            }
            catch (Exception ex)
            {
                UpdateMessage("服务关闭失败！" + Environment.NewLine + ex.Message);
            }
        }

        void UpdateMessage(string message)
        {
            if (CallbackUpdateMessage != null)
            {
                CallbackUpdateMessage(message);
            }
        }


        void LoadRules()
        {

            this.Rules = TextRuleParser.Convert(appConfig);

            if (this.RulesLoadCompleted != null)
            {
                RulesLoadCompleted(this.Rules);
            }
        }


        UrlProcess urlProcess;
        void LoadCmdProcess()
        {
            urlProcess = new UrlProcess();
        }

        void Fiddler_OnBeforeRequest_ChangeSession(Fiddler.Session oSession, ConditionItem condition)
        {
            if (condition.ResultType == urlProcess.Type)
            {
                if (condition.ResultType == ResultEnumType.IP)
                {
                    oSession["custom-target-ip"] = (condition.ActualResult ?? "");
                }
                urlProcess.Execute(oSession, condition);
            }
        }


    }
}
